package org.codingq92.basic.thread;

import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * 继承AQS基础类实现一个自定义的互斥锁~
 *
 * @author CodingQ92
 */
public class MyLock extends AbstractQueuedSynchronizer {

    public static class MyMutexLock extends AbstractQueuedSynchronizer {
        /**
         * 尝试获取独占锁
         *
         * @param arg 获取锁时使用的参数，通常表示获取锁的次数
         * @return 如果成功获取锁则返回true，否则返回false
         */
        @Override
        protected boolean tryAcquire(int arg) {
            // 使用CAS操作尝试将状态从0设置为1，如果成功则表示获取锁成功
            if (compareAndSetState(0, 1)) {
                // 设置当前线程为独占锁的持有者
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            // CAS操作失败，表示锁已被其他线程持有，返回false
            return false;
        }

        /**
         * 尝试释放锁
         *
         * @param arg 释放锁的参数，通常表示释放的次数或资源数量
         * @return boolean 返回true表示锁释放成功，返回false表示锁释放失败
         */
        @Override
        protected boolean tryRelease(int arg) {
            // 检查当前锁状态，如果状态为0表示没有锁可释放，抛出异常
            if (getState() == 0) {
                throw new IllegalMonitorStateException();
            }
            // 清除独占锁的持有线程，并将锁状态设置为0，表示锁已释放
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        /**
         * 判断当前线程是否独占持有锁
         *
         * @return true表示当前线程持有独占锁，false表示未持有
         */
        @Override
        protected boolean isHeldExclusively() {
            // 检查锁的状态是否为1来判断是否被独占持有
            return getState() == 1;
        }
    }

    private final MyMutexLock lock = new MyMutexLock();

    public void lock() {
        lock.acquire(1);
    }

    public void unlock() {
        lock.release(1);
    }

    public boolean isLocked() {
        return lock.isHeldExclusively();
    }

    public static void main(String[] args) throws Exception {
        int[] nums = new int[]{1000};
        ArrayList<Thread> list = new ArrayList<>();
        MyLock myLock = new MyLock();
        for (int i = 0; i < 1000; i++) {
            Thread thread = new Thread(() -> {
                try {
                    myLock.lock();
                    try {
                        TimeUnit.MICROSECONDS.sleep(20);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    nums[0]--;
                } finally {
                    myLock.unlock();
                }
            });
            list.add(thread);
        }
        for (int i = 0; i < 1000; i++) {
            list.get(i).start();
        }
        for (int i = 0; i < 1000; i++) {
            list.get(i).join();
        }
        System.out.println(nums[0]);
    }
}
